import pymysql
import logging
import uuid
import datetime
import time
import hashlib
import functools
import traceback
from flask import jsonify
from hashlib import sha1, md5
from dao.db_connect import db_connect as db
from controller.WXBizDataCrypt import WXBizDataCrypt
import json, requests
from random import Random
import controller.carbon_alipay.AlipayConfig as AlipayConfig

# 请求成功
success = 200
# errors
internal_error = 500  # 服务器错误
identification_error = 100  # 未识别成功
log_error = 101  # 用户登录失败
token_expire_error = 102  # token超时失效
register_error = 103  # 用户名已存在
not_log_error = 104  # 未登录
usr_rank_error = 105  # 用户权限不足

TOKEN_EXPIRE_TIME = 10800


def get_uuid():
    return "".join(str(uuid.uuid4()).split("-")).upper()


class LoginController(object):

    def __init__(self):
        pass

    @classmethod
    def user_login(cls, request):
        logging.debug("working on user_login")
        user_name = request.values.get('user_name')
        password = request.values.get('password')
        logging.debug(f"user_name:{user_name}, password:{password}")

        try:
            query_sql = f"select id, name from user where binary name='{user_name}' and password='{password}'"
            user_info = db.queryOne(query_sql)

            if not user_info:
                resp = {
                    "data": None,
                    "msg": "用户名或密码错误，请重新输入！",
                    "code": log_error
                }
                return jsonify(resp)
            # update token
            user_id = user_info['id']
            user_name = user_info['name']
            token = UserAuth.create_token(user_id)
            timestamp = int(time.time())
            update_token_sql = f"update user set update_time={timestamp}, token='{token}' where id='{user_id}'"
            db.UpdateOrDeleteOrInsert(update_token_sql)

            data = {"user_id": user_info['id'], "token": token, "user_name": user_name}
            code = 200
            msg = "登录成功"
        except Exception as e:
            code = 500
            data = None
            logging.debug(traceback.format_exc())
            msg = "登录失败"

        resp = {
            "data": data,
            "msg": msg,
            "code": code
        }
        return jsonify(resp)

    @classmethod
    def user_register(cls, request):
        logging.debug("working on user_register")
        user_name = request.values.get('user_name')
        password = request.values.get('password')

        try:
            query_sql = f"select id from user where name='{user_name}'"
            user_info = db.queryAll(query_sql)
            if user_info:
                resp = {
                    "data": None,
                    "msg": "用户名已存在，请重新输入！",
                    "code": register_error
                }
                return jsonify(resp)

            create_time = datetime.datetime.now()
            user_id = get_uuid()
            insert_sql = f"insert into user (id, name, password, create_time)" \
                         f"values ('{user_id}', '{user_name}', '{password}', '{create_time}')"
            db.UpdateOrDeleteOrInsert(insert_sql)

            code = 200
            msg = "注册成功"
        except Exception as e:
            code = 500
            logging.debug(traceback.format_exc())
            msg = "注册失败"

        resp = {
            "data": None,
            "msg": msg,
            "code": code
        }
        return jsonify(resp)

    @classmethod
    def modify_password(cls, request):
        logging.debug("working on modify_password")
        user_id = request.values.get('user_id')
        old_password = request.values.get('old_password')
        new_password = request.values.get('new_password')

        try:
            query_sql = f"select id from user where id='{user_id}' and password='{old_password}'"
            user_info = db.queryOne(query_sql)
            if not user_info:
                resp = {
                    "code": log_error,
                    "msg": "原密码错误",
                }
                return jsonify(resp)

            update_sql = f"update user set password='{new_password}' where id='{user_id}'"
            db.UpdateOrDeleteOrInsert(update_sql)
            code = 200
            msg = "密码修改成功"
        except Exception as e:
            code = 500
            msg = "密码修改失败"
            logging.debug(traceback.format_exc())

        resp = {
            "code": code,
            "msg": msg,
        }
        return jsonify(resp)

    @classmethod
    def user_list(cls, request):
        logging.debug("working on user_list")
        login_name = request.values.get('loginName')
        passwd = request.values.get('passwd')
        sql = "select name, password from user"
        status = "success"
        msg = ""
        data = None
        try:
            result = db.queryAll(sql)
            data = result
        except Exception as e:
            status = "fail"
            logging.debug(e)
            msg = "用户名或邮箱或电话号码输入有误"

        if not data:
            status = "fail"
            msg = "用户名或邮箱或电话号码输入有误"
        resp = {
            "datas": data,
            "msg": msg,
            "status": status
        }
        return jsonify(resp)

    @classmethod
    def user_info(cls, request):
        """
        获得用户信息
        """
        logging.debug("working on user_info")
        code = success
        msg = "获取信息成功"
        user_id = request.values.get('user_id')

        try:
            query_sql = f'select id, name, rank, vip_expire, last_expire_time, max_times, current_times ' \
                        f'from user where id = "{user_id}"'
            user_data = db.queryOne(query_sql)
            if user_data is None:
                msg = "用户不存在！"
                code = identification_error
                return jsonify(code=code, data={}, msg=msg)
            user_rank = user_data['rank']
            vip_expire = user_data['vip_expire']
            string_vip_expire = None
            if vip_expire is not None:
                string_vip_expire = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(vip_expire))
                logging.debug("vip_expire:" + str(string_vip_expire))

            if user_rank > 0 and vip_expire is not None and vip_expire < int(time.time()):
                # 刷新数据库
                update_sql = f"update user set rank=0 where id='{user_id}'"
                db.UpdateOrDeleteOrInsert(update_sql)

            # 刷新用户可下载次数
            current = int(time.time())
            max_times = user_data.get('max_times')
            current_times = user_data.get('current_times')
            last_expire_time = user_data.get('last_expire_time')
            if max_times is None:
                max_times = AlipayConfig.default_download_max_times
            if current_times is None:
                current_times = 0
            if last_expire_time is None:
                last_expire_time = current

            monthly_time = 30 * 24 * 3600
            if last_expire_time + monthly_time < current:
                max_times = AlipayConfig.default_download_max_times
                current_times = 0
                time_area = current - last_expire_time
                time_area = int(time_area / monthly_time)
                last_expire_time = last_expire_time + time_area * monthly_time
            # 刷新数据库
            update_sql = f"update user set last_expire_time={last_expire_time}, " \
                         f"max_times={max_times}, current_times={current_times} " \
                         f"where id='{user_id}'"
            db.UpdateOrDeleteOrInsert(update_sql)

        except Exception as e:
            msg = "获取列表失败"
            code = identification_error
            logging.debug(traceback.format_exc())
            return jsonify(code=code, data={}, msg=msg)

        resp = {
            "code": code,
            "msg": msg,
            "data": {
                'user_name': user_data['name'],
                'user_id': user_id,
                'user_rank': user_rank,
                'vip_expire': string_vip_expire,
                'max_times': max_times,
                'current_times': current_times,
            }
        }
        return jsonify(resp)

    @classmethod
    def user_purchased_files(cls, request):
        """
        获得用户已购文档信息
        """
        logging.debug("working on user_purchased_files")
        code = success
        msg = "获取信息成功"
        user_id = request.values.get('user_id')
        row_num = request.values.get('row_num')
        page_num = request.values.get('page_num')
        if row_num is None or len(row_num) == 0:
            row_num = 30
        else:
            row_num = int(row_num)
            row_num = 30 if row_num <= 0 else row_num
        if page_num is None or len(page_num) == 0:
            page_num = 1
        else:
            page_num = int(page_num)
            page_num = 1 if page_num <= 0 else page_num
        try:
            query_sql = f'select id, name from user where id = "{user_id}"'
            user_data = db.queryOne(query_sql)
            if user_data is None:
                msg = "当前用户不存在！"
                code = identification_error
                return jsonify(code=code, data=[], msg=msg)
            user_name = user_data.get("name")

            query_sql = f'select files_info.id, files_info.name, goods.good_price, ' \
                        f'DATE_FORMAT(files_purchased.purchase_time, "%Y-%m-%d %H:%i:%s") as purchase_time ' \
                        f'from files_info, files_purchased, goods ' \
                        f'where files_info.id = files_purchased.fid and files_purchased.uid = "{user_id}" ' \
                        f'and files_info.good_id = goods.good_id order by files_purchased.purchase_time desc'
            file_records = db.queryAll(query_sql)
            for record in file_records:
                good_price = record.get('good_price')
                if good_price is None:
                    good_price = 0
                good_price = float(good_price) / 100
                record['good_price'] = good_price
            page_data = file_records[(page_num - 1) * row_num: page_num * row_num]

        except Exception as e:
            msg = "获取列表失败"
            code = identification_error
            logging.debug(traceback.format_exc())
            return jsonify(code=code, data={}, msg=msg)

        resp = {
            "code": code,
            "msg": msg,
            "data": page_data,
            "user_name": user_name,
            'row_num': row_num,
            'page_num': page_num,
            "total": len(file_records)
        }
        return jsonify(resp)


class UserAuth(object):

    def __init__(self):
        pass

    @classmethod
    def create_token(cls, tenant_id):
        str_list = "".join([tenant_id, "&", str(int(time.time()))])
        token_str = md5(str_list.encode()).hexdigest()

        return token_str

    @classmethod
    def verify_token(cls, client_token, user_id):
        logging.debug("working on verify_token")
        res_user_token_sql = f"select token, update_time from user where id='{user_id}'"
        res_token = db.queryOne(res_user_token_sql)
        if not res_token:
            logging.debug("token fail info: not res_token")
            return not_log_error
        server_token = res_token["token"]
        logging.debug(f'server_token:{server_token}, client_token:{client_token}')
        if client_token != server_token:
            logging.debug("token fail info: client_token != server_token")
            return token_expire_error
        token_running_time = int(time.time()) - res_token["update_time"]
        if token_running_time > TOKEN_EXPIRE_TIME:
            logging.debug("token fail info: token_running_time > token_expire_time")
            return token_expire_error

        return True

    @classmethod
    def login_verify(cls, view_func):
        @functools.wraps(view_func)
        def verify_token_signature():
            from flask import request
            logging.debug("working on login_verify")
            client_token = request.headers.get("Authorization")
            user_id = request.values.get('user_id')
            logging.debug(f'client_token:{client_token}')

            # verify token
            token_result_flag = cls.verify_token(client_token, user_id)

            if token_result_flag == not_log_error:
                return jsonify(code=not_log_error, data=None, msg='用户未登录，请先登录！')
            if token_result_flag == token_expire_error:
                return jsonify(code=token_expire_error, data=None, msg='用户登录状态失效，请重新登录！')

            timestamp = int(time.time())
            update_token_sql = f"update user set update_time={timestamp} where id= '{user_id}'"
            db.UpdateOrDeleteOrInsert(update_token_sql)
            return view_func()

        return verify_token_signature

    @classmethod
    def user_wxlogin(cls, json, request):
        "微信登陆授权界面"
        logging.debug("working on user_wxlogin")
        data = json.loads(request.get_data().decode('utf-8'))  # 将前端Json数据转为字典
        appID = 'wxb086ca4c3dda76e3'  # 开发者关于微信小程序的appID
        appSecret = 'bf6cffa453df943084b3fbb40950aa30'  # 开发者关于微信小程序的appSecret
        code = data['platCode']  # 前端POST过来的微信临时登录凭证code
        encryptedData = data['platUserInfoMap']['encryptedData']
        iv = data['platUserInfoMap']['iv']
        req_params = {
            'appid': appID,
            'secret': appSecret,
            'js_code': code,
            'grant_type': 'authorization_code'
        }
        wx_login_api = 'https://api.weixin.qq.com/sns/jscode2session'
        response_data = requests.get(wx_login_api, params=req_params)  # 向API发起GET请求
        resData = response_data.json()
        openid = resData['openid']  # 得到用户关于s当前小程序的OpenID
        session_key = resData['session_key']  # 得到用户关于当前小程序的会话密钥session_key
        # 加密openid，生成token返回前端
        time_stamp = time.time()
        token = cls.get_token(openid, time_stamp)
        code = success
        msg = "写入数据库成功"
        try:
            pc = WXBizDataCrypt(appID, session_key)  # 对用户信息进行解密
            userinfo = pc.decrypt(encryptedData, iv)  # 获得用户信息
            print(userinfo)
            # 写入数据库
            nickName = data['userInfo']['nickName']
            create_time = int(round(time.time()))
            insert_sql = f'INSERT INTO users (uid, user_name, created_at, token) VALUES("{openid}","{nickName}","{create_time}","{token}")'
            data = db.UpdateOrDeleteOrInsert(insert_sql)
        except Exception as e:
            code = 500
            logging.debug(traceback.format_exc())
            msg = "写入数据库失败"
        resp = {
            "msg": msg,
            "code": code,
            "token": token
        }
        return jsonify(resp)

    @classmethod
    def get_token(cls, string, time_stamp):
        # 使用SHA256加密
        base_str = str(string) + str(int(time_stamp))
        generator = hashlib.sha256()
        generator.update(bytes(base_str, encoding='utf-8'))
        return str(generator.hexdigest())
